package main.filter;


import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

import main.model.Defines;
import main.model.rdf.RDF;
import main.model.rdf.RdfField;
import main.location.Location;

public class FactFilter implements Defines
{	
	private List<RDF> facts = new ArrayList<RDF>(); 
	private List<RDF> filteredFacts = new ArrayList<RDF>(); 
	
	public void setFacts(List<RDF> _facts)
	{
		facts = _facts;
	}
	
	public List<RDF> getFacts()
	{
		return facts;
	}
	
	public List<RDF> getResult()
	{
		return filteredFacts;
	}
	
	public void setFilteredFacts(List<RDF> _filteredFacts)
	{
		filteredFacts = _filteredFacts;
	}

	public void clearResult()
	{
		filteredFacts.clear();
	}
	
	public void filterOr(RDF filter)
	{
		for (RDF fact : facts)
		{
			if (compare(fact, filter))
				filteredFacts.add(fact);
		}
	}
	
	public void filterAnd(RDF filter)
	{
		Iterator<RDF> i = filteredFacts.iterator();
		
		while (i.hasNext()) 
		{
			if (!compare(i.next(), filter)) 
				i.remove();
		}
	}

	public void filterOr(RDF filter, FactFilter subResult)
	{
		for (RDF subFilter : subResult.getResult())
		{			
			filterOr(generateSubRDF(filter, subFilter));
		}
	}
	
	public void filterAnd(RDF filter, FactFilter subResult)
	{
		for (RDF subFilter : subResult.getResult())
		{			
			filterAnd(generateSubRDF(filter, subFilter));
		}
	}
	
	public void filterDate(String after, String before)
	{
		int dateAfter = dateStringToInt(after);
		int dateBefore = dateStringToInt(before);
		int factDate;

		for (RDF fact : facts)
		{
			if (fact.getObject().getTypeName().equals(DATE_TYPE))
			{
				factDate = dateStringToInt(fact.getObject().getName());
				if (factDate == 0) continue;

				if (((dateAfter == 0) || (factDate > dateAfter)) &&
						((dateBefore == 0) || (factDate < dateBefore)))
				filteredFacts.add(fact);
			}
		}
	}
	
	public void filterLocation(Location location, double radius)
	{		
		RDF factLongitude;
		RDF filter;
		Location factLocation;
		
		for (RDF factLatitude : facts)
		{
			if (factLatitude.getObject().getTypeName().equals(LATITUDE_TYPE))
			{
				filter = new RDF(factLatitude.getSubject(), 
						new RdfField(EMPTY_STRING, EMPTY_STRING),
						new RdfField(EMPTY_STRING, LONGITUDE_TYPE));
				factLongitude = findFact(filter);
				
				try
				{
					factLocation = new Location(factLatitude.getObject().getName(), 
							factLongitude.getObject().getName());
					
					if (location.distanceBetween(factLocation) < radius)
					{
						filteredFacts.add(factLatitude);
						filteredFacts.add(factLongitude);
					}
				}
				finally {}
			}
		}
	}
	
	public RDF findFact(RDF filter)
	{
		for (RDF fact : facts)
		{
			if (compare(fact, filter))
				return fact;
		}
		
		return null;
	}
	
	public static boolean compare(RDF fact, RDF filter)
	{
		if (!compareFields(fact.getSubject(), filter.getSubject())) return false;
		if (!compareFields(fact.getProperty(), filter.getProperty())) return false;
		if (!compareFields(fact.getObject(), filter.getObject())) return false;
		
		return true;
	}
	
	private static boolean compareFields(RdfField fact, RdfField filter)
	{
		if (!compareStrings(fact.getName(), filter.getName())) 
			return false;

		if (compareStrings(fact.getTypeName(), filter.getTypeName()))
			return true;
		
		for (String subType : fact.getTypeSubTypes())
		{
			if (compareStrings(subType, filter.getTypeName()))
				return true;
		}
		
		return false;
	}

    private static boolean compareStrings(String fact, String filter)
    {
            if (filter.equals(EMPTY_STRING)) return true;
            if (filter.equals("")) return true;
            return filter.equals(fact);
    }
    
    private RDF generateSubRDF(RDF filter, RDF subFilter)
    {
    	return new RDF(filter.getSubject(), filter.getProperty(), 
    			subFilter.getSubject());
    }

    private static int dateStringToInt(String date)
    {
    	try
    	{
    		String[] d = date.split("\\.");
    		if (d.length != 3) return 0;
    		
	    	int day = Integer.parseInt(d[0]);
	    	int month = Integer.parseInt(d[1]);
	    	int year = Integer.parseInt(d[2]);
    	
	    	return year*10000 + month*100 + day;
    	}
    	
    	catch (Exception e)
    	{
    		return 0;
    	}
    }
}
